// arch-tag: 5a27e2b7-2304-4b2c-bc07-d3670e9ecbf5
package de.yvert.jingle.hdr;

import de.yvert.jingle.Image2D;
import de.yvert.jingle.ImageType;

/**
 * Contains color samples for a rectangular area - simply put: an image.<br />
 * Uses an array of float for its internal storage.
 * Colors are stored in RGBA order, e.g. data[0] is red, data[1] is green, 
 * data[2] is blue and data[3] is alpha.
 * The row order is top-down (the 0th row is stored first).
 * 
 * This class provides a number of data manipulation functions.
 * 
 * @author Ulf Ochsenfahrt
 */
public class HdrImage2D implements Image2D
{

protected float[] data;
protected int width, height;

public HdrImage2D(int width, int height, float[] data)
{
	if ((width <= 0) || (height <= 0))
		throw new IllegalArgumentException("An image cannot have negative dimensions.");
	if (data.length != 4*width*height)
		throw new IllegalArgumentException("data doesn't have correct length!");
	this.width = width;
	this.height = height;
	this.data = data;
}

public HdrImage2D(int width, int height)
{ this(width, height, new float[width*height*4]); }

public HdrImage2D(HdrImage2D other)
{
	this(other.width, other.height);
	System.arraycopy(other.data, 0, this.data, 0, other.data.length);
}

public ImageType getType()
{ return ImageType.RGBA_2D_FLOAT; }

public int getWidth()
{ return width;  }

public int getHeight()
{ return height; }

public float[] getData()
{ return data; }

public HdrImage2D flipY()
{
	float[] temp = new float[4*width];
	for (int y = 0; y < height/2; y++)
	{
		System.arraycopy(data, 4*width*y, temp, 0, 4*width);
		System.arraycopy(data, 4*width*(height-1-y), data, 4*width*y, 4*width);
		System.arraycopy(temp, 0, data, 4*width*(height-1-y), 4*width);
	}
	return this;
}

public HdrImage2D toHDR()
{ return this; }


private int checkRange(int x, int y)
{
	if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
		throw new IndexOutOfBoundsException("x,y = "+x+","+y);
	return (y*width+x)*4;
}


public float getRed(int x, int y)
{ return data[checkRange(x, y)+0]; }

public float getGreen(int x, int y)
{ return data[checkRange(x, y)+1]; }

public float getBlue(int x, int y)
{ return data[checkRange(x, y)+2]; }

public float getAlpha(int x, int y)
{ return data[checkRange(x, y)+3]; }

public void getRGB(int x, int y, float[] rgb)
{ System.arraycopy(data, checkRange(x, y), rgb, 0, 3); }

public void getRGBA(int x, int y, float[] rgba)
{ System.arraycopy(data, checkRange(x, y), rgba, 0, 4); }


public void setRGBA(int x, int y, float r, float g, float b, float a)
{
	int offset = checkRange(x, y);
	data[offset+0] = r;
	data[offset+1] = g;
	data[offset+2] = b;
	data[offset+3] = a;
}

public void setRGB(int x, int y, float r, float g, float b)
{
	int offset = checkRange(x, y);
	data[offset+0] = r;
	data[offset+1] = g;
	data[offset+2] = b;
	data[offset+3] = 1;
}

public void setRGB(int x, int y, float[] rgb)
{
	int offset = checkRange(x, y);
	data[offset+0] = rgb[0];
	data[offset+1] = rgb[1];
	data[offset+2] = rgb[2];
	data[offset+3] = 1;
}

public void setRGBA(int x, int y, float[] rgba)
{
	int offset = checkRange(x, y);
	data[offset+0] = rgba[0];
	data[offset+1] = rgba[1];
	data[offset+2] = rgba[2];
	data[offset+3] = rgba[3];
}

public void setBGRint(int x, int y, int rgb)
{
	int offset = checkRange(x, y);
	data[offset+0] = ( rgb        & 0xff)/255.0f;
	data[offset+1] = ((rgb >>  8) & 0xff)/255.0f;
	data[offset+2] = ((rgb >> 16) & 0xff)/255.0f;
	data[offset+3] = 1;
}

public void setRGBint(int x, int y, int rgb)
{
	int offset = checkRange(x, y);
	data[offset+2] = ( rgb        & 0xff)/255.0f;
	data[offset+1] = ((rgb >>  8) & 0xff)/255.0f;
	data[offset+0] = ((rgb >> 16) & 0xff)/255.0f;
	data[offset+3] = 1;
}

public void setABGRint(int x, int y, int rgba)
{
	int offset = checkRange(x, y);
	data[offset+0] = ( rgba        & 0xff)/255.0f;
	data[offset+1] = ((rgba >>  8) & 0xff)/255.0f;
	data[offset+2] = ((rgba >> 16) & 0xff)/255.0f;
	data[offset+3] = ((rgba >> 24) & 0xff)/255.0f;
}

public void setARGBint(int x, int y, int rgba)
{
	int offset = checkRange(x, y);
	data[offset+2] = ( rgba        & 0xff)/255.0f;
	data[offset+1] = ((rgba >>  8) & 0xff)/255.0f;
	data[offset+0] = ((rgba >> 16) & 0xff)/255.0f;
	data[offset+3] = ((rgba >> 24) & 0xff)/255.0f;
}


public void add(HdrImage2D other)
{
	for (int i = 0; i < data.length; i++)
		data[i] += other.data[i];
}

public void divide(float divisor)
{
	for (int i = 0; i < data.length; i++)
		data[i] /= divisor;
}


// bulk set methods
// TODO: should be deprecated (create LdrImage2D and convert to HdrImage2D)
public void decodeRGB(byte[] arr)
{
	for (int i = 0; i < arr.length/3; i++)
	{
		data[4*i+0] = (arr[3*i+0] & 0xff)/255.0f;
		data[4*i+1] = (arr[3*i+1] & 0xff)/255.0f;
		data[4*i+2] = (arr[3*i+2] & 0xff)/255.0f;
		data[4*i+3] = 1;
	}
}

public void decodeBGR(byte[] arr)
{
	for (int i = 0; i < arr.length/3; i++)
	{
		data[4*i+2] = (arr[3*i+0] & 0xff)/255.0f;
		data[4*i+1] = (arr[3*i+1] & 0xff)/255.0f;
		data[4*i+0] = (arr[3*i+2] & 0xff)/255.0f;
		data[4*i+3] = 1;
	}
}

public void decodeBGRA(byte[] arr)
{
	for (int i = 0; i < arr.length/4; i++)
	{
		data[4*i+2] = (arr[4*i+0] & 0xff)/255.0f;
		data[4*i+1] = (arr[4*i+1] & 0xff)/255.0f;
		data[4*i+0] = (arr[4*i+2] & 0xff)/255.0f;
		data[4*i+3] = (arr[4*i+3] & 0xff)/255.0f;
	}
}

}
